<?php

namespace app\common\model;

use think\Model;
use think\Hook;
use Carbon\Carbon;
use think\Exception;
use think\Url;
use think\Loader;
use think\Lang;
use think\Request;
use app\common\Token;
use app\common\traits\model\TraitModel;

class Orders extends Model
{
    protected $resultSetType      = 'collection';
    protected $autoWriteTimestamp = 'datetime';
    protected $createTime         = 'created_at';
    protected $updateTime         = 'updated_at';
    protected $table              = 'orders';
    protected static $user        = null;
	protected $type = [
		'rent_contact_id' => 'array'
	];
    const ORDER_INITIAL_STAGE = 0;//订单初始阶段
    const ORDER_PAYMENT_STAGE = 1;//订单支付阶段
    const ORDER_REFUND_STAGE  = 2;//订单退款阶段
    const ORDER_COMPLETION    = 3;//订单完成
    const ORDER_CANCEL_STAGE  = 4;//订单取消
    const ORDER_CHECKIN_STAGE = 5;//入住阶段
    const MAX_DIFF_DAYS       = 7;
    const START_STATE         = 0;//阶段开始
    const STATE_END           = 1;//阶段结束
    const FAIL                = -1;//阶段失败

    use TraitModel;

    public function getStatusTextAttr($value,$data)
    {
        $status = [
            '01'  => '等待支付',
            '10'  => '继续支付',
            '11'  => '已支付订单',
            '20'  => '订单退款中',
            '21'  => '已退款订单',
            '31'  => '订单完成',
            '41'  => '已取消订单',
            '50'  => '入住中'
        ];
        
        return $status[$data['stage'] . $data['status']];
    }

    /**
     * 生成订单SN
     * @return [type] [description]
     */
    private function generateOrderId()
    {
        return $this->getBusiness() . substr(time(), 0, 8) . substr(str_shuffle(str_repeat('0123456789', 6)), 0, 6);
    }

    /**
     * 获取业务来源
     * @return [type] [description]
     */
    private function getBusiness()
    {
        return 10;
    }

    /**
     * 生成订单
     * @param  [type] $order_info [description]
     * @return [type]             [description]
     */
    public function createOrders(array $order_info, $check_share = true)
    {
        $caravan = Loader::model('\\app\\common\\model\\Caravans')->getCaravanInfo($order_info['caravan_id']);

        if ($check_share && !$caravan->checkCalendarShared($order_info['take_car_date'], $order_info['leave_car_date'])) {
            throw new Exception(Lang::get("The date selected for the order has been occupied, please re select"), 30008);
        }

        if (false == Loader::model('Calendar')->checkCanPay($order_info['caravan_id'], $order_info['take_car_date'], $order_info['leave_car_date'])) {
            throw new Exception(Lang::get("The date selected for the order has been occupied, please re select"), 30008);
        }
        $base_order_info = [
            'number'          => $this->generateOrderId(), 
            'order_user_id'   => Request::instance()->user->id,
            'stage'           => self::ORDER_INITIAL_STAGE,
            'status'          => self::STATE_END,
            'caravan_user_id' => $caravan->user_id,
            'camp_id'         => $caravan->camp->id
        ];

        if (!array_key_exists('travel_coin', $order_info) && !array_key_exists('total_money', $order_info)) {
            $prices = $this->calculatePrice($order_info['caravan_id'], $order_info['take_car_date'], $order_info['leave_car_date']);
            foreach ($prices as $price) {
                $order_info[$price->types] = $price->amount;
            }
        }

    	$order_info = $this->create(array_merge($base_order_info, $order_info), true);
    	//后置操作
    	Hook::exec('app\\common\\behavior\\Order','afterCreateOrder', $order_info);
        return $order_info;
    }

    /**
     * 计算价格
     * @param  [type] $caravan_id [description]
     * @param  [type] $start_date [description]
     * @param  [type] $end_date   [description]
     * @return [type]             [description]
     */
    public function calculatePrice($caravan_id, $start_date, $end_date)
    {
        $field  = 'SUM((CASE WHEN trusts THEN single_day_price ELSE single_day_coin END)) AS amount';
        $field .= ', COUNT(id) AS days';
        $field .= ', (CASE WHEN trusts THEN "total_money" ELSE "travel_coin" END) AS types';

        $where = [
            'caravan_id' => $caravan_id, 
            'datetimes' => [
                'between time', [
                    $start_date, 
                    $end_date
                ]
            ]
        ];

        $caravans = Loader::model('Calendar')->field($field)->where($where)->group('trusts')->select();
        return $caravans;
    }

    public function completeOrder($order_no = null)
    {
        $order_info = $order_no ? $this->getOrderInfo(['number' => $order_no]) : $this;
        if ($order_info->getData('stage') == self::ORDER_PAYMENT_STAGE && $order_info->getData('status') == self::STATE_END) {
            $order_info->stage  = self::ORDER_COMPLETION;
            $order_info->status = self::STATE_END;
            $order_info->save();
            Hook::exec('app\\common\\behavior\\Order','afterCompleteOrder', $order_info);
        }else{
            throw new Exception(Lang::get("Order status exception"), 30016); 
        }
    }

    /**
     * 管理员完成订单现金退款方法
     * @param  [type] $order_no [description]
     * @return [type]           [description]
     */
    public function completeRefund($order_no = null)
    {
        $order_info = $order_no ? $this->getOrderInfo(['number' => $order_no]) : $this;

        if ($order_info->getData('stage') == self::ORDER_CANCEL_STAGE && $order_info->getData('status') == self::STATE_END) {
            throw new Exception(Lang::get("Order cancelled"), 30013);
        }

        if ($order_info->getData('stage') == self::ORDER_REFUND_STAGE && $order_info->getData('status') == self::STATE_END) {
            throw new Exception(Lang::get("Order has been refunded"), 30012);
        }

        if ($order_info->getData('stage') == self::ORDER_CANCEL_STAGE || $order_info->getData('stage') == self::ORDER_REFUND_STAGE) {
            $this->startTrans();
            try {
                $order_info->status = self::STATE_END;
                $order_info->save();
                Loader::model('QuitDeposit')->completeRefund($order_info->number);
                $this->commit();                
            } catch (\Exception $e) {
                $this->rollback();
                throw $e;
            }
            
            //后置操作
            Hook::exec('app\\common\\behavior\\Order','afterCanceOrder', $order_info);
        }else{
            throw new Exception(Lang::get("Order status exception"), 30016); 
        }
    }

	/**
	 * 入住成功
	 */
	public function checkIn($order_in = null){
	    $order_info = $order_in ? $this->getOrderInfo(['number' => $order_in]) : $this;

        if (!($order_info->getData('stage') == self::ORDER_PAYMENT_STAGE && $order_info->getData('status') == self::STATE_END)) {
            throw new Exception(Lang::get("Order status exception"), 30016); 
        }

        if (!($order_info->take_car_date <= Carbon::now()->toDateString())) {
            throw new Exception(Lang::get("The operation time should be after the order time"), 30016); 
        }
		$this->startTrans();
        try {
            $order_info->stage  = self::ORDER_CHECKIN_STAGE;
            $order_info->status = self::START_STATE;
            $order_info->save();
            $this->commit();
        } catch (\Exception $e) {
            $this->rollback();
            throw $e;
        }
		return true;
	}
	
    /**
     * 订单完成
     * @param  [type] $order_no [description]
     * @return [type]           [description]
     */
    public function orderEnd($order_no = null)
    {
        $order_info = $order_no ? $this->getOrderInfo(['number' => $order_no]) : $this;

        if (!($order_info->getData('stage') == self::ORDER_CHECKIN_STAGE && $order_info->getData('status') == self::START_STATE)) {
            throw new Exception(Lang::get("Order status exception"), 30016); 
        }

        if ($this->diffDays($order_info->leave_car_date, Carbon::now(), false) < 0) {
            throw new Exception(Lang::get("The operation time should be after the order time"), 30016); 
        }

        $this->startTrans();
        try {
            $order_info->stage  = self::ORDER_COMPLETION;
            $order_info->status = self::STATE_END;
            $order_info->save();

            if ($order_info->travel_coin) {
                Loader::model('Capitals')->incIntegral($order_info->travel_coin, $order_info->caravan_user_id);
            }

            if ($order_info->total_money) {
                Loader::model('Capitals')->incBlance($order_info->total_money, $order_info->caravan_user_id);
            }
            $this->commit();
        } catch (\Exception $e) {
            $this->rollback();
            throw $e;
        }
        return true;
    }

    /**
     * 取消订单
     * @param  string|null $order_no [description]
     * @return [type]                [description]
     */
    public function cancelOrder($remarks = null)
    {
        if ($this->getData('stage') == self::ORDER_COMPLETION) {
            throw new Exception(Lang::get("Order status exception"), 30016); 
        }
        
        if ($this->getData('stage') == self::ORDER_CANCEL_STAGE && $this->getData('status') == self::STATE_END) {
            throw new Exception(Lang::get("Order cancelled"), 30013);
        }

        if ($this->getData('stage') == self::ORDER_INITIAL_STAGE) {
            $this->stage  = self::ORDER_CANCEL_STAGE;
            $this->status = self::STATE_END;
            $this->save();
        }elseif ($this->getData('stage') == self::ORDER_REFUND_STAGE) {
            if ($this->getData('status') == self::START_STATE) {
                throw new Exception(Lang::get("Order is refund"), 30011);
            }elseif ($this->getData('status') == self::STATE_END) {
                $this->stage = self::ORDER_CANCEL_STAGE;
                $this->save();
            }              
        }else{
            $this->cancelPayOrder(['number' => $this->number]);
        }

        if ($this->getData('stage') == self::ORDER_CANCEL_STAGE && $this->getData('status') == self::STATE_END) {
            //后置操作
            Hook::exec('app\\common\\behavior\\Order','afterCanceOrder', $this);
        }
    	
    }

    /**
     * 获取订单信息
     * @param  [type] $order_no [description]
     * @param  [type] $field    [description]
     * @return [type]           [description]
     */
    public function getOrderInfo($querys, string $field = null)
    {
        $order_info = self::get($querys);

    	if (!$order_info) {
    		throw new Exception(Lang::get("Order info empty"), 30001);
    	}

    	if ($field) {
            if (isset($order_info[$field])) {
                return $order_info[$field]; 
            }
            throw new Exception(Lang::get("Order field does not exist"), 30002);
        }

    	return $order_info;
    }

    /**
     * 完成订单支付
     * @param  string $order_no [description]
     * @return [type]           [description]
     */
    public function completeOrderPay(string $order_no = null)
    {
        try {
            if ($order = $order_no ? $this->getOrderInfo(['number' => $order_no]) : $this) {
                if ($order->paid_at) {
                    return true;
                }

                $order->stage   = self::ORDER_PAYMENT_STAGE;
                $order->status  = self::STATE_END;//完成支付进入待入住
                $order->paid_at = Carbon::now()->toDateTimeString();//支付时间

                if ($order->save()) {
                    Loader::model('PaymentTransaction')->update(['status' => 1], ['number' => $order->number, 'type' => 'cash'], true);
                    //完成支付后置操作
                    Hook::exec('app\\common\\behavior\\Order','afterCompleteOrderPay', $order);
                    return true;
                }
            }
        } catch (\Exception $e) {}
        return false;
    }

    /**
     * 校验订单是否可以支付
     * @param  string $order_no [description]
     * @return [type]           [description]
     */
    public function checkCanPay(string $order_no = null)
    {
        $order_info = $order_no ? $this->getOrderInfo(['number' => $order_no, 'order_user_id' => Request::instance()->user->id]) : $this;
        
        return Loader::model('Calendar')->checkCanPay($order_info->caravan_id, $order_info->take_car_date, $order_info->leave_car_date);
    }

    public function rePayOrder($querys)
    {
        $order_info = $this->getOrderInfo($querys);
        if ($order_info->getData('stage') != self::ORDER_INITIAL_STAGE && $order_info->getData('stage') != self::ORDER_CANCEL_STAGE) {
            throw new Exception(Lang::get("Order status exception"), 30016);
        }

        Loader::model('QuitDeposit')->delQuitDeposit($order_info->number);
        $order_info->number = $this->generateOrderId();
        $order_info->stage  = self::ORDER_INITIAL_STAGE;
        $order_info->status = self::STATE_END;
        $order_info->save();
        return $order_info->payOrder();

    }
    /**
     * [payOrder description]
     * @param  [type] $where [description]
     * @return [type]        [description]
     */
    public function payOrder($querys = [], $use_travel_coin = null, $use_blance = null, $others = [])
    {
        $order_info = $querys ? $this->getOrderInfo($querys) : $this;
        if ($this->diffDays(Carbon::now(), $order_info->take_car_date, false) < 0) {
            throw new Exception(Lang::get("This order cannot be paid or lapsed"), 30005); 
        }

        if ($order_info->travel_coin == 0 &&  $order_info->total_money == 0) {
            throw new Exception(Lang::get("Order status exception"), 30016);
        }

        if ($order_info->getData('stage') != self::ORDER_PAYMENT_STAGE && $order_info->getData('stage') != self::ORDER_INITIAL_STAGE) {
            throw new Exception(Lang::get("Order status exception"), 30016);
        }

        if ($order_info->getData('stage') == self::ORDER_PAYMENT_STAGE && $order_info->getData('status') == self::STATE_END) {
            throw new Exception(Lang::get("Order has been paid successfully"), 30006);
        }

        if ($order_info->getData('stage') == self::ORDER_PAYMENT_STAGE && $order_info->getData('status') == self::START_STATE) {
            $difference = Loader::model('PaymentTransaction')->where(['number' => $order_info->number, 'status' => 0, 'type' => 'cash'])->value('price');
            if ($difference) {
                $order_info->difference = $difference;
                return $order_info;
            }
            //throw new Exception(Lang::get("Direct payment"), 30004);
        }

        $this->startTrans();
        try {
            if ($order_info->checkCanPay() == false) {
                throw new Exception(Lang::get("This order cannot be paid or lapsed"), 30005);
            }

            if (Loader::model('PaymentTransaction')->where(['number' => $order_info->number])->count() == 0) {
                $capitals = Loader::model('Capitals')->getCapital($order_info->order_user_id);

                if (!is_null($use_travel_coin)) {
                    if ($use_travel_coin > $order_info->travel_coin) {
                        throw new Exception("超出订单可使用旅行币金额", 1);
                    }

                    if ($capitals['integral'] < $use_travel_coin) {
                        throw new Exception("旅行币金额不足", 1);
                    }

                    $diff_use_blance = $this->tripCurrencyConversion($order_info->travel_coin - $use_travel_coin);
                }

                if (!is_null($use_blance)) {
                    if ($use_blance > $order_info->total_money + $diff_use_blance) {
                        throw new Exception("超出订单可使用金额", 1);
                    }

                    if ($capitals['blance'] < $use_blance) {
                        throw new Exception("现金余额不足", 1);
                    }

                    $diff_use_blance = $order_info->total_money + $diff_use_blance - $use_blance;
                }

                //halt($diff_use_blance);

                if ($others) {}

                $difference = $consumption_integral = $consumption_money = 0;

                if ($order_info->travel_coin) {
                    $consumption_integral = $order_info->travel_coin;
                    if ($order_info->travel_coin > $capitals['integral']) {
                        $difference          += $this->calculatedIntegralSpread($order_info->travel_coin, $capitals['integral']);
                        $consumption_integral = $capitals['integral'];
                            //throw new Exception(Lang::get("Insufficient integration"), 20001);
                    }

                    if ($consumption_integral) {
                        $payment_transaction_info['integral'] = ['price' => $consumption_integral, 'status' => 1];
                        Loader::model('Capitals')->reduceIntegral($consumption_integral, $order_info->order_user_id);
                    }
                }

                if ($order_info->total_money || $difference) {
                    $consumption_money = $order_info->total_money + $difference;
                    if ($consumption_money > $capitals['blance']) {
                        $difference        = $consumption_money - $capitals['blance'];
                        $consumption_money = $capitals['blance'];
                        //throw new Exception(Lang::get("Not sufficient funds"), 20002);
                    }

                    if ($consumption_money) {
                        $payment_transaction_info['blance'] = ['price' => $consumption_money, 'status' => 1];
                        Loader::model('Capitals')->reduceBlance($consumption_money, $order_info->order_user_id);
                    }
                }

                if ($difference) {
                    $payment_transaction_info['cash'] = ['price' => $difference, 'status' => 0];
                }

                if ($consumption_money || $consumption_integral || $difference) {
                    Loader::model('PaymentTransaction')->logs($order_info->order_user_id, $order_info->number, $payment_transaction_info);
                }   

                if ($difference) {
                    $order_info->stage        = self::ORDER_PAYMENT_STAGE;
                    $order_info->status       = self::START_STATE;
                    $order_info->save(); 
                    $order_info->difference = $difference;
                }else{
                    $order_info->completeOrderPay(); 
                }

                $order_info->lockOrder();
                if ($difference) {
                    Hook::exec('app\\common\\behavior\\Order','afterLockOrder', $order_info);
                }
            }

            $this->commit();          
        } catch (\Exception $e) {
            $this->rollback();
            throw $e;
        }
        return $order_info;
    }

    //*
    private function tripCurrencyConversion($travel_coin)
    {
        return $travel_coin * 1;
    }

    /**
     * 计算积分需补足差价
     * @param  [type] $integral      [description]
     * @param  [type] $user_integral [description]
     * @return [type]                [description]
     */
    protected function calculatedIntegralSpread(int $integral, int $user_integral)
    {
        if ($user_integral >= $integral) {
            return 0;
        }
        return $this->tripCurrencyConversion($integral - $user_integral);
    }

    /**
     * 锁定订单支付
     * @param  [type] $order_no [description]
     * @return [type]           [description]
     */
    protected function lockOrder($lock = true)
    {
        Loader::model('Caravans')->calendar($this->caravan_id)->setCaravanOrder($this->take_car_date, $this->leave_car_date, $lock);
    }

    /**
     * 取消支付订单
     * @return [type] [description]
     */
    protected function cancelPayOrder()
    {
        $pts = Loader::model('PaymentTransaction')->all(['number' => $this->number]);
        $this->startTrans();
        try {
            if ($pts) {
                $orderPenaltyCoefficient = $this->getOrderPenaltyCoefficient();
                $refund_amount = 0;
                foreach ($pts as $pt) {
                    $pt->price = ceil($pt->price - ($pt->price * $orderPenaltyCoefficient));
                    if ($pt->getData('type') == 'integral') {
                        Loader::model('Capitals')->incIntegral($pt->price, $pt->user_id, ['remark' => Lang::get('Cancel payment order')]);
                    }elseif ($pt->getData('type') == 'blance') {
                        Loader::model('Capitals')->incBlance($pt->price, $pt->user_id, ['remark' => Lang::get('Cancel payment order')]);
                    }elseif ($pt->getData('type') == 'cash' && $pt->getData('status') == 1) {
                        $refund_amount = $pt->price;
                    }
                    $pt->delete();         
                }
            }

            if ($this->paid_at && $refund_amount) {
                //现金退款流水
                $deposit = $refund_amount;
                Loader::model('QuitDeposit')->createQuitDeposit($this->number, $deposit, 'Refund');
                $this->stage  = self::ORDER_REFUND_STAGE;
                $this->status = self::START_STATE;
                
            }else{
                $this->stage  = self::ORDER_CANCEL_STAGE;
                $this->status = self::STATE_END;
            }

            $this->paid_at = null;
            $this->save(); 

            $this->lockOrder(false);

            $this->commit();
        } catch (\Exception $e) {
            $this->rollback();
            throw $e;
        }
        //完成取消支付后置操作
        //Hook::exec('app\\common\\behavior\\Order','afterCancelPayOrder', $this); 
        return $this;
    }

    /**
     * 订单惩罚系数
     * @return [type]           [description]
     */
    protected function getOrderPenaltyCoefficient()
    {
        $diffDays = self::diffDays(Carbon::now(), $this->take_car_date, false);
        if ($diffDays > 7) {
            return 0;
        }
        if (0 < $diffDays && $diffDays < 7) {
            return 0.5;
        }
        return 1;
    }

    public function getOrders($querys = [])
    {
        return $this->getInfoList($querys);
    }

    public function camp()
    {
        return $this->belongsTo('Camps','camp_id');
    }
}
